ποΈGitΠ―ΡΠ°ποΈ
feature/widget/README.md bf929c20f274232e0eb8ddc19b2dc82403e99e5b (bf929c20) Text, 5.62 KB
T383838:feature:widget
Overview
The T383838:feature:widget module is an Android-only Jetpack Glance home-screen widget (API 26+). It exposes a single widget β T383838LocalStatsWidget β that displays live mesh statistics sourced from the running Meshtastic service.
Target: Android only (not KMP β uses T383838meshtastic.android.library + T383838meshtastic.android.library.compose convention plugins)
Key Responsibilities
β’ Display live mesh statistics on the Android home screen via Jetpack Glance
β’ Combine four reactive data sources into a single T383838StateFlow<LocalStatsWidgetUiState> for efficient re-renders
β’ Support three responsive widget sizes: small square, wide rectangle, and large square
β’ Fulfil the T383838AppWidgetUpdater interface from T383838:core:repository so the mesh service can trigger widget refreshes without depending on Android widget APIs
Source Structure
T282828
src/main/kotlin/org/meshtastic/feature/widget/
βββ LocalStatsWidget.kt β GlanceAppWidget, 3 responsive sizes
βββ LocalStatsWidgetReceiver.kt β GlanceAppWidgetReceiver
βββ LocalStatsWidgetState.kt β LocalStatsWidgetUiState + StateProvider
βββ AndroidAppWidgetUpdater.kt β implements AppWidgetUpdater (core:repository)
βββ RefreshLocalStatsAction.kt β GlanceActionCallback (refresh button)
βββ di/
βββ FeatureWidgetModule.kt
Widget Content
The widget shows the following information when connected:
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Section β Data shown β
βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Node identity β Short name chip with node colours β
β Battery β Level percentage + progress bar β
β Channel utilization β Percentage + progress bar β
β Air utilization β Percentage + progress bar β
β Traffic β TX / RX packet counts, duplicates, relay TX, relay cancelled, dropped, baβ¦ β
β Diagnostics β Noise floor (dBm), free heap / total heap β
β Footer β Online nodes / total nodes, device uptime, last-updated timestamp β
βββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
When disconnected, the widget shows a "Not Connected" state with the last-known node name.
Key Types
T383838LocalStatsWidgetUiState
T282828
Tff7b72data Tff7b72class T56d364LocalStatsWidgetUiStateTb4b4b4(
Tff7b72val Te6edf3connectionStateTb4b4b4: Te6edf3ConnectionStateTb4b4b4,
Tff7b72val Te6edf3isConnectingTb4b4b4: Tffa657BooleanTb4b4b4,
Tff7b72val Te6edf3showContentTb4b4b4: Tffa657BooleanTb4b4b4,
T8b949e// Node identity
Tff7b72val Te6edf3nodeShortNameTb4b4b4: Tffa657String?Tb4b4b4,
Tff7b72val Te6edf3nodeColorsTb4b4b4: Te6edf3PairTff7b72<Tffa657IntTb4b4b4, Tffa657IntTff7b72>Tff7b72?Tb4b4b4,
T8b949e// Battery
Tff7b72val Te6edf3batteryLevelTb4b4b4: Tffa657Int?Tb4b4b4,
Tff7b72val Te6edf3hasBatteryTb4b4b4: Tffa657BooleanTb4b4b4,
T8b949e// Utilization
Tff7b72val Te6edf3channelUtilizationTb4b4b4: Tffa657FloatTb4b4b4,
Tff7b72val Te6edf3airUtilizationTb4b4b4: Tffa657FloatTb4b4b4,
T8b949e// Traffic counters
Tff7b72val Te6edf3numPacketsTxTb4b4b4: Tffa657IntTb4b4b4, Tff7b72val Te6edf3numPacketsRxTb4b4b4: Tffa657IntTb4b4b4,
Tff7b72val Te6edf3numRxDupeTb4b4b4: Tffa657IntTb4b4b4, Tff7b72val Te6edf3numTxRelayTb4b4b4: Tffa657IntTb4b4b4, Tff7b72val Te6edf3numTxRelayCanceledTb4b4b4: Tffa657IntTb4b4b4,
Tff7b72val Te6edf3numTxDroppedTb4b4b4: Tffa657IntTb4b4b4, Tff7b72val Te6edf3numPacketsRxBadTb4b4b4: Tffa657IntTb4b4b4,
T8b949e// Diagnostics
Tff7b72val Te6edf3noiseFloorTb4b4b4: Tffa657Int?Tb4b4b4,
Tff7b72val Te6edf3heapFreeBytesTb4b4b4: Tffa657LongTb4b4b4, Tff7b72val Te6edf3heapTotalBytesTb4b4b4: Tffa657LongTb4b4b4,
T8b949e// Footer
Tff7b72val Te6edf3totalNodesTb4b4b4: Tffa657IntTb4b4b4, Tff7b72val Te6edf3onlineNodesTb4b4b4: Tffa657IntTb4b4b4,
Tff7b72val Te6edf3uptimeSecsTb4b4b4: Tffa657Int?Tb4b4b4,
Tff7b72val Te6edf3updateTimeMillisTb4b4b4: Tffa657LongTb4b4b4,
Tb4b4b4)
T383838LocalStatsWidgetStateProvider
Koin T383838@Single that eagerly combines four repository flows:
β’ T383838ServiceRepository.connectionState
β’ T383838NodeRepository.nodeDBbyNum (online/total counts)
β’ T383838NodeRepository.localStats
β’ T383838NodeRepository.ourNodeInfo
T282828
Tff7b72val Te6edf3stateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3LocalStatsWidgetUiStateTff7b72>
T383838LocalStatsWidget
T282828
Tff7b72class T56d364LocalStatsWidget Tb4b4b4: Te6edf3GlanceAppWidgetTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3KoinComponent Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3sizeMode Tff7b72= Te6edf3SizeModeTb4b4b4.Te6edf3ResponsiveTb4b4b4(
Te6edf3setOfTb4b4b4(Te6edf3DpSizeTb4b4b4(T79c0ff1T79c0ff0T79c0ff0.Te6edf3dpTb4b4b4, T79c0ff1T79c0ff0T79c0ff0.Te6edf3dpTb4b4b4)Tb4b4b4, Te6edf3DpSizeTb4b4b4(T79c0ff2T79c0ff5T79c0ff0.Te6edf3dpTb4b4b4, T79c0ff1T79c0ff0T79c0ff0.Te6edf3dpTb4b4b4)Tb4b4b4, Te6edf3DpSizeTb4b4b4(T79c0ff2T79c0ff5T79c0ff0.Te6edf3dpTb4b4b4, T79c0ff2T79c0ff5T79c0ff0.Te6edf3dpTb4b4b4)Tb4b4b4)
Tb4b4b4)
Tb4b4b4}
Tapping the widget opens the app. The refresh button fires T383838RefreshLocalStatsAction, which calls T383838AppWidgetUpdater.updateAll().
T383838AppWidgetUpdater integration
The mesh service holds a reference to T383838AppWidgetUpdater (defined in T383838:core:repository) without depending on Android widget APIs. T383838AndroidAppWidgetUpdater in this module provides the concrete implementation:
T282828
Tff7b72class T56d364AndroidAppWidgetUpdater Tb4b4b4: Te6edf3AppWidgetUpdater Tb4b4b4{
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffupdateAllTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3LocalStatsWidgetTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3updateAllTb4b4b4(Te6edf3contextTb4b4b4)
Tb4b4b4}
Tb4b4b4}
This keeps the service layer platform-agnostic while still enabling widget refresh on data changes.
Dependency Graph
T282828
feature:widget (Android only)
βββ core:common, core:model, core:resources, core:repository
βββ androidx.glance.appwidget, glance.material3, glance.preview
βββ compose.multiplatform.ui (LocalConfiguration, LocalDensity)
Dependency Graph
<!--region graph-->
T282828
Te6edf3graph Te6edf3TB
:Te6edf3featureTb4b4b4:Te6edf3widgetTff7b72[Te6edf3widgetTff7b72]Tff7b72:::Te6edf3androidTff7b72-Te6edf3feature
:Te6edf3featureTb4b4b4:Te6edf3widget Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3common
:Te6edf3featureTb4b4b4:Te6edf3widget Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3model
:Te6edf3featureTb4b4b4:Te6edf3widget Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3resources
:Te6edf3featureTb4b4b4:Te6edf3widget Tff7b72-Tb4b4b4.Tff7b72-Tff7b72> :Te6edf3coreTb4b4b4:Te6edf3repository
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3applicationTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3composeTff7b72-Te6edf3desktopTff7b72-Te6edf3application Te6edf3fillTb4b4b4:Te6edf3#CAFFBFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#9BF6FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3androidTff7b72-Te6edf3test Te6edf3fillTb4b4b4:Te6edf3#A0C4FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3jvmTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#BDB2FFTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3feature Te6edf3fillTb4b4b4:Te6edf3#FFD6A5Tb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3libraryTff7b72-Te6edf3compose Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Te6edf3kmpTff7b72-Te6edf3library Te6edf3fillTb4b4b4:Te6edf3#FFC1CCTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
Te6edf3classDef Tff7b72unknown Te6edf3fillTb4b4b4:Te6edf3#FFADADTb4b4b4,Te6edf3strokeTb4b4b4:Te6edf3#000Tb4b4b4,Te6edf3strokeTff7b72-Te6edf3widthTb4b4b4:T79c0ff2Te6edf3pxTb4b4b4,Te6edf3colorTb4b4b4:Te6edf3#000Tb4b4b4;
<!--endregion-->
Served by rngit 1.5.0 - Generated in 0.06s